import BlockManager from "./BlockManager"
import Listeners from "./Listeners"

/**
 * 创建一个使用浏览器 history Api 的 history 对象
 * @param {Object} options 配置项
 * @returns {Object} history 对象
 */

export default function createBrowserHistory(options = {}) {
    const {
        basename = '',
        forceRefresh = false,
        getUserConfirmation = (message, callback) => callback(window.confirm(message)),
        keyLength = 6,
    } = options

    const BM = new BlockManager(getUserConfirmation)

    /**
     * 用于实现 push replace，提取出的重复代码
     * @param {*} path 
     * @param {*} state 
     * @param {*} action 
     */
    const changePageByAction = (path, state, action) => {
        const type = {
            'PUSH': 'pushState',
            'REPLACE': 'replaceState',
        }[action]
        const pathInfo = handlePathAndState(path, state, basename)
        // 触发阻塞
        BM.triggerBlock(history.location, history.action, () => {
            window.history[type]({
                key: createKey(keyLength),
                state: pathInfo.state
            }, null, pathInfo.path)
            // 改变action属性
            history.action = action
            // 改变location
            history.location = createLocation(basename)
            // 触发监听器
            Listeners.triggerListener(history.location, history.action)
            // 强制刷新页面
            if (forceRefresh) {
                window.location.href = pathInfo.path
            }
        })
    }

    // 添加监听
    const addDomListener = () => {
        // popstate 仅能监听前进后退，用户对地址hash的改变
        // 无法监听到 pushState 和 replaceState
        window.addEventListener('popstate', () => {
            // 改变action属性
            history.action = 'POP'
            // 改变location
            history.location = createLocation(basename)
            // 触发监听器
            Listeners.triggerListener(history.location, history.action)
        })
    }
    addDomListener()

    // 返回history对象  
    const history = {
        action: 'POP',
        block: (msg) => {
            return BM.block(msg)
        },
        createHref: () => '',
        go: () => window.history.go(),
        goBack: () => window.history.back(),
        goForward: () => window.history.forward(),
        length: window.history.length,
        /**
         * 添加一个监听器
         * @param {*} listener 监听器
         * @returns 返回一个取消监听的函数
         */
        listen: (listener) => {
            return Listeners.addListener(listener)
        },
        location: createLocation(basename),
        /**
         * 向地址栈中加入一个新的地址
         * @param {*} path 要加入的地址 可以是字符串或者对象
         * @param {Object} state 要加入的地址的state，如果第一个参数是对象，则该参数无效
         */
        push: (path, state) => changePageByAction(path, state, 'PUSH'),
        /**
         * 替换地址栈中当前指针指向的地址
         * @param {*} path 要替换的地址 可以是字符串或者对象
         * @param {Object} state 要替换的地址的state，如果第一个参数是对象，则该参数无效
         */
        replace: (path, state) => changePageByAction(path, state, 'REPLACE'),
    }
    return history
}

/**
 * 创建一个location对象
 */
function createLocation(basename = '') {
    // 使用正则从pathname中移除包含basename的部分
    const pathname = window.location.pathname.replace(new RegExp(`^${basename}`), '');
    const location = {
        hash: window.location.hash,
        search: window.location.search,
        pathname,
    }
    // 处理state
    const { state: historyState } = window.history;
    let state;
    if (historyState === null) {
        state = undefined
    } else if (typeof (historyState) !== 'object') {
        state = historyState
    } else {
        if ('key' in historyState) {
            location.key = historyState.key
            state = historyState.state
        } else {
            state = historyState
        }
    }
    location.state = state;
    return location
}

/**
 * 生成一个随机的key
 */
function createKey(length) {
    return Math.random().toString(36).substr(2, length)
}

/**
 * 根据 path 和 state 创建一个统一的对象格式
 * @param {*} path 
 * @param {*} state 
 * @returns 
 */
function handlePathAndState(path, state, basename) {
    if (typeof path === 'string') {
        return {
            path: basename + path,
            state
        }
    } else if (typeof path === 'object') {
        const {
            pathname,
            search = '',
            hash = '',
        } = path
        const completePath = basename + pathname + handleSearchParams(search) + handleHash(hash)
        return {
            path: completePath,
            state: path.state
        }
    } else {
        throw new Error('path must be a string or an object')
    }
}

// 处理路径后的search参数
function handleSearchParams(search = '') {
    if (search.charAt(0) !== '?') {
        return '?' + search
    }
    return search
}

// 处理路径中的hash参数
function handleHash(hash = '') {
    if (hash.charAt(0) !== '#') {
        return '#' + hash
    }
    return hash
}

const history = createBrowserHistory({
    basename: '/news',
    forceRefresh: false,
})
window.h = history